home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / pico / browse.c < prev    next >
C/C++ Source or Header  |  1996-05-28  |  46KB  |  2,041 lines

  1. #if    !defined(lint) && !defined(DOS)
  2. static char rcsid[] = "$Id: browse.c,v 4.62 1996/05/29 00:05:04 mikes Exp $";
  3. #endif
  4. /*
  5.  * Program:    Routines to support file browser in pico and Pine composer
  6.  *
  7.  *
  8.  * Michael Seibel
  9.  * Networks and Distributed Computing
  10.  * Computing and Communications
  11.  * University of Washington
  12.  * Administration Builiding, AG-44
  13.  * Seattle, Washington, 98195, USA
  14.  * Internet: mikes@cac.washington.edu
  15.  *
  16.  * Please address all bugs and comments to "pine-bugs@cac.washington.edu"
  17.  *
  18.  *
  19.  * Pine and Pico are registered trademarks of the University of Washington.
  20.  * No commercial use of these trademarks may be made without prior written
  21.  * permission of the University of Washington.
  22.  * 
  23.  * Pine, Pico, and Pilot software and its included text are Copyright
  24.  * 1989-1996 by the University of Washington.
  25.  * 
  26.  * The full text of our legal notices is contained in the file called
  27.  * CPYRIGHT, included with this distribution.
  28.  *
  29.  *
  30.  * NOTES:
  31.  *
  32.  *   Misc. thoughts (mss, 5 Apr 92)
  33.  * 
  34.  *      This is supposed to be just a general purpose browser, equally
  35.  *    callable from either pico or the pine composer.  Someday, it could
  36.  *     even be used to "wrap" the unix file business for really novice 
  37.  *      users.  The stubs are here for renaming, copying, creating directories,
  38.  *      deleting, undeleting (thought is delete just moves files to 
  39.  *      ~/.pico.deleted directory or something and undelete offers the 
  40.  *      files in there for undeletion: kind of like the mac trashcan).
  41.  *
  42.  *   Nice side effects
  43.  *
  44.  *      Since the full path name is always maintained and referencing ".." 
  45.  *      stats the path stripped of its trailing name, the unpleasantness of 
  46.  *      symbolic links is hidden.  
  47.  *
  48.  *   Fleshed out the file managements stuff (mss, 24 June 92)
  49.  *
  50.  *
  51.  */
  52. #include <stdio.h>
  53. #include <ctype.h>
  54. #include <errno.h>
  55. #include "osdep.h"
  56. #include "pico.h"
  57. #include "estruct.h"
  58. #include "edef.h"
  59. #include "efunc.h"
  60.  
  61. #ifndef    _WINDOWS
  62.  
  63. #if    defined(bsd) || defined(lnx)
  64. extern int errno;
  65. #endif
  66.  
  67.  
  68. /*
  69.  * directory cell structure
  70.  */
  71. struct fcell {
  72.     char *fname;                /* file name            */
  73.     unsigned mode;                /* file's mode           */
  74.     char size[16];                /* file's size in s    */
  75.     struct fcell *next;
  76.     struct fcell *prev;
  77. };
  78.  
  79.  
  80. /*
  81.  * master browser structure
  82.  */
  83. static struct bmaster {
  84.     struct fcell *head;                /* first cell in list  */
  85.     struct fcell *top;                /* cell in top left    */
  86.     struct fcell *current;            /* currently selected  */
  87.     int    longest;                /* longest file name   */
  88.     int       fpl;                    /* file names per line */
  89.     int    cpf;                    /* chars / file / line */
  90.     char   dname[NLINE];            /* this dir's name     */
  91.     char   *names;                /* malloc'd name array */
  92. } *gmp;                        /* global master ptr   */
  93.  
  94.  
  95. /*
  96.  * title for exported browser display
  97.  */
  98. static    char    *browser_title = NULL;
  99.  
  100.  
  101. #ifdef    ANSI
  102.     struct bmaster *getfcells(char *);
  103.     int    PaintCell(int, int, int, struct fcell *, int);
  104.     int    PaintBrowser(struct bmaster *, int, int *, int *);
  105.     int    BrowserKeys(void);
  106.     int    layoutcells(struct bmaster *);
  107.     int    percdircells(struct bmaster *);
  108.     int    PlaceCell(struct bmaster *, struct fcell *, int *, int *);
  109.     int    zotfcells(struct fcell *);
  110.     int    zotmaster(struct bmaster **);
  111.     struct fcell *FindCell(struct bmaster *, char *);
  112.     int    sisin(char *, char *);
  113.     int    BrowserAnchor(char *);
  114.     void   ClearBrowserScreen(void);
  115.     void   BrowserRunChild(char *);
  116.     int    LikelyASCII(char *);
  117. #else
  118.     struct bmaster *getfcells();
  119.     int    PaintCell();
  120.     int    PaintBrowser();
  121.     int    BrowserKeys();
  122.     int    percdircells();
  123.     int    PlaceCell();
  124.     int    zotfcells();
  125.     int    zotmaster();
  126.     struct fcell *FindCell();
  127.     int    sisin();
  128.     int    BrowserAnchor();
  129.     void   ClearBrowserScreen();
  130.     void   BrowserRunChild();
  131.     int    LikelyASCII();
  132. #endif
  133.  
  134.  
  135. static    KEYMENU menu_browse[] = {
  136.     {"?", "Get Help", KS_SCREENHELP},    {NULL, NULL, KS_NONE},
  137.     {NULL, NULL, KS_NONE},        {"-", "Prev Pg", KS_PREVPAGE},
  138.     {"D", "Delete", KS_NONE},        {"C","Copy", KS_NONE},
  139.     {NULL, NULL, KS_NONE},        {NULL, NULL, KS_NONE},
  140.     {"W", "Where is", KS_NONE},        {"Spc", "Next Pg", KS_NEXTPAGE},
  141.     {"R", "Rename", KS_NONE},        {NULL, NULL, KS_NONE}
  142. };
  143. #define    QUIT_KEY    1
  144. #define    EXEC_KEY    2
  145. #define    GOTO_KEY    6
  146. #define    SELECT_KEY    7
  147. #define    PICO_KEY    11
  148.  
  149.  
  150. /*
  151.  * Default pager used by the stand-alone file browser.
  152.  */
  153. #define    BROWSER_PAGER    ((gmode & MDFKEY) ? "pine -k -F" : "pine -F")
  154.  
  155.  
  156. /*
  157.  * function key mappings for callable browser
  158.  */
  159. static int  bfmappings[2][12][2] = { { { F1,  '?'},    /* stand alone */
  160.                        { F2,  NODATA },    /* browser function */
  161.                        { F3,  'q'},    /* key mappings... */
  162.                        { F4,  'v'},
  163.                        { F5,  'l'},
  164.                        { F6,  'w'},
  165.                        { F7,  '-'},
  166.                        { F8,  ' '},
  167.                        { F9,  'd'},
  168.                        { F10, 'r'},
  169.                        { F11, 'c'},
  170.                        { F12, 'e'} },
  171.                      { { F1,  '?'},    /* callable browser */
  172.                        { F2,  NODATA },    /* function key */
  173.                        { F3,  'e'},    /* mappings... */
  174.                        { F4,  's'},
  175.                        { F5,  NODATA },
  176.                        { F6,  'w'},
  177.                        { F7,  '-'},
  178.                        { F8,  ' '},
  179.                        { F9,  'd'},
  180.                        { F10, 'r'},
  181.                        { F11, 'c'},
  182.                        { F12, NODATA } } };
  183.  
  184.  
  185. /*
  186.  * Browser help for pico (pine composer help handed to us by pine)
  187.  */
  188. static char *BrowseHelpText[] = {
  189. "Help for Browse Command",
  190. "  ",
  191. "\tPico's file browser is used to select a file from the",
  192. "\tfile system for inclusion in the edited text.",
  193. "  ",
  194. "~\tBoth directories and files are displayed.  Press ~S",
  195. "~\tor ~R~e~t~u~r~n to select a file or directory.  When a file",
  196. "\tis selected during the \"Read File\" command, it is",
  197. "\tinserted into edited text.  Answering \"yes\" to the",
  198. "\tverification question after a directory is selected causes",
  199. "\tthe contents of that directory to be displayed for selection.",
  200. "  ",
  201. "\tThe file named \"..\" is special, and means the \"parent\"",
  202. "\tof the directory being displayed.  Select this directory",
  203. "\tto move upward in the directory tree.",
  204. "  ",
  205. "End of Browser Help.",
  206. "  ",
  207. NULL
  208. };
  209.  
  210. static char *sa_BrowseHelpText[] = {
  211. "Help for File Browser",
  212. "  ",
  213. "\tThe File Browser is used to display and manipulate files.",
  214. "  ",
  215. "~\tBoth directories and files are displayed.  Press ~V",
  216. "~\tor ~R~e~t~u~r~n to display the selected directory or view a",
  217. "~\ttext file's contents.  Other commands are available to",
  218. "~\tEdit, Copy, Rename, and Delete the selected (highlighted)",
  219. "~\tfile.",
  220. "  ",
  221. "\tThe file named \"..\" is special, and means the \"parent\"",
  222. "\tof the directory being displayed.  Select this directory",
  223. "\tto move upward in the directory tree.",
  224. "  ",
  225. "End of File Browser Help.",
  226. "  ",
  227. NULL
  228. };
  229.  
  230.  
  231.  
  232. /*
  233.  * pico_file_browse - Exported version of FileBrowse below.
  234.  */
  235. pico_file_browse(pdata, dir, fn, sz, flags)
  236.     PICO *pdata;
  237.     char *dir, *fn, *sz;
  238.     int   flags;
  239. {
  240.     int  rv;
  241.     char title_buf[64];
  242.  
  243.     Pmaster = pdata;
  244.     gmode = pdata->pine_flags | MDEXTFB;
  245.     km_popped = 0;
  246.  
  247.     /* only init screen bufs for display and winch handler */
  248.     if(!vtinit())
  249.       return(-1);
  250.  
  251.     if(Pmaster){
  252.     term.t_mrow = Pmaster->menu_rows;
  253.     if(Pmaster->oper_dir)
  254.       strncpy(opertree, Pmaster->oper_dir, NLINE);
  255.     
  256.     if(*opertree)
  257.       fixpath(opertree, NLINE);
  258.     }
  259.  
  260.     /* install any necessary winch signal handler */
  261.     ttresize();
  262.  
  263.     sprintf(title_buf, "%s FILE", pdata->pine_anchor);
  264.     set_browser_title(title_buf);
  265.     rv = FileBrowse(dir, fn, sz, flags);
  266.     set_browser_title(NULL);
  267.     vttidy();            /* clean up tty handling */
  268.     zotdisplay();        /* and display structures */
  269.     Pmaster = NULL;        /* and global config structure */
  270.     return(rv);
  271. }
  272.  
  273.  
  274.  
  275. /*
  276.  * FileBrowse - display contents of given directory dir
  277.  *
  278.  *        intput:  
  279.  *             dir points to initial dir to browse.
  280.  *             fn  initial file name. 
  281.  *
  282.  *         returns:
  283.  *                   dir points to currently selected directory (without
  284.  *            trailing file system delimiter)
  285.  *                   fn  points to currently selected file
  286.  *                   sz  points to size of file if ptr passed was non-NULL
  287.  *             flags
  288.  *
  289.  *                   1 if a file's been selected
  290.  *                   0 if no files seleted
  291.  *                  -1 if there where problems
  292.  */
  293. FileBrowse(dir, fn, sz, flags)
  294. char *dir, *fn, *sz;            /* dir, name and optional size */
  295. int   flags;
  296. {
  297.     int status, i, j, c, new_c;
  298.     int row, col, crow, ccol;
  299.     char *p, child[NLINE], tmp[NLINE];
  300.     struct bmaster *mp, *getfcells();
  301.     struct fcell *tp;
  302. #ifdef MOUSE
  303.     MOUSEPRESS mousep;
  304. #endif
  305.  
  306.     child[0] = '\0';
  307.  
  308.     if((gmode&MDTREE) && !in_oper_tree(dir)){
  309.     emlwrite("\007Can't read outside of %s in restricted mode", opertree);
  310.     sleep(2);
  311.     return(0);
  312.     }
  313.  
  314.     if(gmode&MDGOTO){
  315.     /* fix up function key mapping table */
  316.     /* fix up key menu labels */
  317.     }
  318.  
  319.     /* build contents of cell structures */
  320.     if((gmp = getfcells(dir)) == NULL)
  321.       return(-1);
  322.  
  323.     /* paint screen */
  324.     PaintBrowser(gmp, 0, &crow, &ccol);
  325.  
  326.     while(1){                        /* the big loop */
  327.     if(!(gmode&MDSHOCUR)){
  328.         crow = term.t_nrow-term.t_mrow;
  329.         ccol = 0;
  330.     }
  331.  
  332.     movecursor(crow, ccol);
  333.  
  334.     if(km_popped){
  335.         km_popped--;
  336.         if(km_popped == 0)
  337.           /* cause bottom three lines to repaint */
  338.           PaintBrowser(gmp, 0, &crow, &ccol);
  339.     }
  340.  
  341.     if(km_popped){  /* temporarily change to cause menu to paint */
  342.         term.t_mrow = 2;
  343.         movecursor(term.t_nrow-2, 0);  /* clear status line */
  344.         peeol();
  345.         BrowserKeys();
  346.         term.t_mrow = 0;
  347.     }
  348.  
  349.     (*term.t_flush)();
  350.  
  351. #ifdef MOUSE
  352.     mouse_in_content(K_MOUSE, -1, -1, 0, 0);
  353.     register_mfunc(mouse_in_content,2,0,term.t_nrow-(term.t_mrow+1),
  354.         term.t_ncol);
  355. #endif
  356.     c = GetKey();
  357. #ifdef    MOUSE
  358.     clear_mfunc(mouse_in_content);
  359. #endif
  360.  
  361.     if(Pmaster){
  362.         if(Pmaster->newmail && (c == NODATA || time_to_check())){
  363.         if((*Pmaster->newmail)(c == NODATA ? 0 : 2, 1) >= 0){
  364.             if(km_popped){        /* restore display */
  365.             km_popped = 0;
  366.             PaintBrowser(gmp, 0, &crow, &ccol);
  367.             }
  368.  
  369.             clearcursor();
  370.             mlerase();
  371.             (*Pmaster->showmsg)(c);
  372.             mpresf = 1;
  373.         }
  374.  
  375.         clearcursor();
  376.         movecursor(crow, ccol);
  377.         }
  378.     }
  379.     else{
  380.         if(timeout && (c == NODATA || time_to_check()))
  381.           if(pico_new_mail())
  382.         emlwrite("You may possibly have new mail.", NULL);
  383.     }
  384.  
  385.     if(km_popped)
  386.       switch(c){
  387.         case NODATA:
  388.         case (CTRL|'L'):
  389.           km_popped++;
  390.           break;
  391.         
  392.         default:
  393.           /* clear bottom three lines */
  394.           movecursor(term.t_nrow-2, 0);
  395.           peeol();
  396.           movecursor(term.t_nrow-1, 0);
  397.           peeol();
  398.           movecursor(term.t_nrow, 0);
  399.           peeol();
  400.           break;
  401.       }
  402.  
  403.     if(c == NODATA)                /* GetKey timed out */
  404.       continue;
  405.  
  406.     if(mpresf){                /* blast old messages */
  407.         if(mpresf++ > MESSDELAY){        /* every few keystrokes */
  408.         mlerase();
  409.         }
  410.         }
  411.  
  412.                         /* process commands */
  413.     switch(new_c = normalize_cmd(c,bfmappings[(gmode&MDBRONLY)?0:1],2)){
  414.  
  415.       case K_PAD_RIGHT:            /* move right */
  416.       case (CTRL|'@'):
  417.       case (CTRL|'F'):            /* forward  */
  418.       case 'n' :
  419.       case 'N' :
  420.         if(gmp->current->next == NULL){
  421.         (*term.t_beep)();
  422.         break;
  423.         }
  424.  
  425.         PlaceCell(gmp, gmp->current, &row, &col);
  426.         PaintCell(row, col, gmp->cpf, gmp->current, 0);
  427.         gmp->current = gmp->current->next;
  428.         if(PlaceCell(gmp, gmp->current, &row, &col)){
  429.         PaintBrowser(gmp, 1, &crow, &ccol);
  430.         }
  431.         else{
  432.         PaintCell(row, col, gmp->cpf, gmp->current, 1);
  433.         crow = row;
  434.         ccol = col;
  435.         }
  436.         break;
  437.  
  438.       case K_PAD_LEFT:                /* move left */
  439.       case (CTRL|'B'):                /* back */
  440.       case 'p' :
  441.       case 'P' :
  442.         if(gmp->current->prev == NULL){
  443.         (*term.t_beep)();
  444.         break;
  445.         }
  446.  
  447.         PlaceCell(gmp, gmp->current, &row, &col);
  448.         PaintCell(row, col, gmp->cpf, gmp->current, 0);
  449.         gmp->current = gmp->current->prev;
  450.         if(PlaceCell(gmp, gmp->current, &row, &col)){
  451.         PaintBrowser(gmp, 1, &crow, &ccol);
  452.         }
  453.         else{
  454.         PaintCell(row, col, gmp->cpf, gmp->current, 1);
  455.         crow = row;
  456.         ccol = col;
  457.         }
  458.         break;
  459.  
  460.       case (CTRL|'A'):                /* beginning of line */
  461.         tp = gmp->current;
  462.         i = col;
  463.         while(i > 0){
  464.         i -= gmp->cpf;
  465.         if(tp->prev != NULL)
  466.           tp = tp->prev;
  467.         }
  468.         PlaceCell(gmp, gmp->current, &row, &col);
  469.         PaintCell(row, col, gmp->cpf, gmp->current, 0);
  470.         gmp->current = tp;
  471.         if(PlaceCell(gmp, tp, &row, &col)){
  472.         PaintBrowser(gmp, 1, &crow, &ccol);
  473.         }
  474.         else{
  475.         PaintCell(row, col, gmp->cpf, gmp->current, 1);
  476.         crow = row;
  477.         ccol = col;
  478.         }
  479.         break;
  480.  
  481.       case (CTRL|'E'):                /* end of line */
  482.         tp = gmp->current;
  483.         i = col + gmp->cpf;
  484.         while(i+gmp->cpf <= gmp->cpf * gmp->fpl){
  485.         i += gmp->cpf;
  486.         if(tp->next != NULL)
  487.           tp = tp->next;
  488.         }
  489.  
  490.         PlaceCell(gmp, gmp->current, &row, &col);
  491.         PaintCell(row, col, gmp->cpf, gmp->current, 0);
  492.         gmp->current = tp;
  493.         if(PlaceCell(gmp, tp, &row, &col)){
  494.         PaintBrowser(gmp, 1, &crow, &ccol);
  495.         }
  496.         else{
  497.         PaintCell(row, col, gmp->cpf, gmp->current, 1);
  498.         crow = row;
  499.         ccol = col;
  500.         }
  501.         break;
  502.  
  503.       case (CTRL|'V'):                /* page forward */
  504.       case ' ':
  505.       case K_PAD_NEXTPAGE :
  506.       case K_PAD_END :
  507.         tp = gmp->top;
  508.         i = term.t_nrow - term.t_mrow - 2;
  509.  
  510.         while(i-- && tp->next != NULL){
  511.         j = 0;
  512.         while(++j <= gmp->fpl  && tp->next != NULL)
  513.           tp = tp->next;
  514.         }
  515.  
  516.         if(tp == NULL)
  517.           continue;
  518.  
  519.         PlaceCell(gmp, gmp->current, &row, &col);
  520.         PaintCell(row, col, gmp->cpf, gmp->current, 0);
  521.         gmp->current = tp;
  522.         if(PlaceCell(gmp, tp, &row, &col)){
  523.         PaintBrowser(gmp, 1, &crow, &ccol);
  524.         }
  525.         else{
  526.         PaintCell(row, col, gmp->cpf, gmp->current, 1);
  527.         crow = row;
  528.         ccol = col;
  529.         }
  530.         break;
  531.  
  532.       case '-' :
  533.       case (CTRL|'Y'):                /* page backward */
  534.       case K_PAD_PREVPAGE :
  535.       case K_PAD_HOME :
  536.         tp = gmp->top;
  537.         i = term.t_nrow - term.t_mrow - 4;
  538.         while(i-- && tp != NULL){
  539.         j = gmp->fpl;
  540.         while(j-- && tp != NULL)
  541.           tp = tp->prev;
  542.         }
  543.  
  544.         if(tp || (gmp->current != gmp->top)){    /* clear old hilite */
  545.         PlaceCell(gmp, gmp->current, &row, &col);
  546.         PaintCell(row, col, gmp->cpf, gmp->current, 0);
  547.         }
  548.  
  549.         if(tp)                    /* new page ! */
  550.         gmp->current = tp;
  551.         else if(gmp->current != gmp->top)        /* goto top of page */
  552.         gmp->current = gmp->top;
  553.         else                    /* do nothing */
  554.           continue;
  555.  
  556.         if(PlaceCell(gmp, gmp->current, &row, &col)){
  557.         PaintBrowser(gmp, 1, &crow, &ccol);
  558.         }
  559.         else{
  560.         PaintCell(row, col, gmp->cpf, gmp->current, 1);
  561.         crow = row;
  562.         ccol = col;
  563.         }
  564.  
  565.         break;
  566.  
  567.       case K_PAD_DOWN :
  568.       case (CTRL|'N'):                /* next */
  569.         tp = gmp->current;
  570.         i = gmp->fpl;
  571.         while(i--){
  572.         if(tp->next == NULL){
  573.             (*term.t_beep)();
  574.             break;
  575.         }
  576.         else
  577.           tp = tp->next;
  578.         }
  579.         if(i != -1)                    /* can't go down */
  580.           break;
  581.  
  582.         PlaceCell(gmp, gmp->current, &row, &col);
  583.         PaintCell(row, col, gmp->cpf, gmp->current, 0);
  584.         gmp->current = tp;
  585.         if(PlaceCell(gmp, tp, &row, &col)){
  586.         PaintBrowser(gmp, 1, &crow, &ccol);
  587.         }
  588.         else{
  589.         PaintCell(row, col, gmp->cpf, gmp->current, 1);
  590.         crow = row;
  591.         ccol = col;
  592.         }
  593.         break;
  594.  
  595.       case K_PAD_UP :
  596.       case (CTRL|'P'):                /* previous */
  597.         tp = gmp->current;
  598.         i = gmp->fpl;
  599.         while(i-- && tp)
  600.           tp = tp->prev;
  601.  
  602.         if(tp == NULL)
  603.           break;
  604.  
  605.         PlaceCell(gmp, gmp->current, &row, &col);
  606.         PaintCell(row, col, gmp->cpf, gmp->current, 0);
  607.         gmp->current = tp;
  608.         if(PlaceCell(gmp, tp, &row, &col)){
  609.         PaintBrowser(gmp, 1, &crow, &ccol);
  610.         }
  611.         else{
  612.         PaintCell(row, col, gmp->cpf, gmp->current, 1);
  613.         crow = row;
  614.         ccol = col;
  615.         }
  616.         break;
  617.  
  618. #ifdef MOUSE        
  619.       case K_MOUSE:
  620.         mouse_get_last (NULL, &mousep);
  621.         if (mousep.doubleclick) {
  622.         goto Selected;
  623.         }
  624.         else {
  625.         row = mousep.row -= 2;        /* Adjust for header*/
  626.         col = mousep.col;
  627.         i = row * gmp->fpl + (col / gmp->cpf);    /* Count from top */
  628.         tp = gmp->top;                /* start at top. */
  629.         for (; i > 0 && tp != NULL; --i)    /* Count cells. */
  630.           tp = tp->next;
  631.         if (tp != NULL) {            /* Valid cell? */
  632.             PlaceCell(gmp, gmp->current, &row, &col);
  633.             PaintCell(row, col, gmp->cpf, gmp->current, 0);
  634.             gmp->current = tp;
  635.             if(PlaceCell(gmp, tp, &row, &col)){
  636.             PaintBrowser(gmp, 1, &crow, &ccol);
  637.             }
  638.             else{
  639.             PaintCell(row, col, gmp->cpf, gmp->current, 1);
  640.             crow = row;
  641.             ccol = col;
  642.             }
  643.         }
  644.             }
  645.         break;
  646. #endif
  647.  
  648.       case 'e':                    /* exit or edit */
  649.       case 'E':
  650.         if(gmode&MDBRONLY){                /* run "pico" */
  651.         sprintf(child, "%s%c%s", gmp->dname, C_FILESEP,
  652.             gmp->current->fname);
  653.         /* make sure selected isn't a directory or executable */
  654.         if(!LikelyASCII(child)){
  655.             emlwrite("Can't edit non-text file.  Try Launch.", NULL);
  656.             break;
  657.         }
  658.  
  659.         sprintf(tmp, "pico%s%s%s %s",
  660.             (gmode & MDFKEY) ? " -f" : "",
  661.             (gmode & MDSHOCUR) ? " -g" : "",
  662.             (gmode & MDMOUSE) ? " -m" : "",
  663.             child);
  664.         BrowserRunChild(tmp);        /* spawn pico */
  665.         PaintBrowser(gmp, 0, &crow, &ccol);    /* redraw browser */
  666.         }
  667.         else{
  668.         zotmaster(&gmp);
  669.         return(0);
  670.         }
  671.  
  672.         break;
  673.  
  674.       case 'q':                        /* user exits wrong */
  675.       case 'Q':
  676.         if(gmode&MDBRONLY){
  677.         zotmaster(&gmp);
  678.         return(0);
  679.         }
  680.  
  681.         emlwrite("\007Unknown command '%c'", (void *)c);
  682.         break;
  683.  
  684.       case 'l':                        /* run Command */
  685.       case 'L':
  686.         if(!(gmode&MDBRONLY)){
  687.         emlwrite("\007Unknown command '%c'", (void *)c);
  688.         break;
  689.         }
  690.  
  691. /* add subcommands to invoke pico and insert selected filename */
  692. /* perhaps: add subcmd to scroll command history */
  693.  
  694.         tmp[0] = '\0';
  695.         i = 0;
  696.         sprintf(child, "%s%c%s", gmp->dname, C_FILESEP,
  697.             gmp->current->fname);
  698.         while(!i){
  699.         static EXTRAKEYS opts[] = {
  700.             {"^X", "Add Name", CTRL|'X', KS_NONE},
  701.             {NULL, NULL, 0, KS_NONE},
  702.         };
  703.  
  704.         status = mlreply("Command to execute: ",
  705.                  tmp, NLINE, QNORML, opts);
  706.         switch(status){
  707.           case HELPCH:
  708.             emlwrite("\007No help yet!", NULL);
  709. /* remove break and sleep after help text is installed */
  710.             sleep(3);
  711.             break;
  712.           case (CTRL|'X'):
  713.             strcat(tmp, child);
  714.             break;
  715.           case (CTRL|'L'):
  716.             PaintBrowser(gmp, 0, &crow, &ccol);
  717.             break;
  718.           case ABORT:
  719.             emlwrite("Command cancelled", NULL);
  720.             i++;
  721.             break;
  722.           case FALSE:
  723.           case TRUE:
  724.             i++;
  725.  
  726.             if(tmp[0] == '\0'){
  727.             emlwrite("No command specified", NULL);
  728.             break;
  729.             }
  730.  
  731.             BrowserRunChild(tmp);
  732.             PaintBrowser(gmp, 0, &crow, &ccol);
  733.             break;
  734.           default:
  735.             break;
  736.         }
  737.         }
  738.  
  739.         BrowserKeys();
  740.         break;
  741.  
  742.       case 'd':                    /* delete */
  743.       case 'D':
  744.         if(gmp->current->mode == FIODIR){
  745. /* BUG: if dir is empty it should be deleted */
  746.         emlwrite("\007Can't delete a directory", NULL);
  747.         break;
  748.         }
  749.  
  750.         if(gmode&MDSCUR){                /* not allowed! */
  751.         emlwrite("Delete not allowed in restricted mode",NULL);
  752.         break;
  753.         }
  754.  
  755.         sprintf(child, "%s%c%s", gmp->dname, C_FILESEP, 
  756.             gmp->current->fname);
  757.  
  758.         i = 0;
  759.         while(i++ < 2){        /* verify twice!! */
  760.         if(i == 1){
  761.             if(fexist(child, "w", (long *)NULL) != FIOSUC)
  762.               strcpy(tmp, "File is write protected! OVERRIDE");
  763.             else
  764.               sprintf(tmp, "Delete file \"%.*s\"", NLINE - 20, child);
  765.         }
  766.         else
  767.           strcpy(tmp, "File CANNOT be UNdeleted!  Really delete");
  768.  
  769.         if((status = mlyesno(tmp, FALSE)) != TRUE){
  770.             emlwrite((status ==  ABORT)
  771.                    ? "Delete Cancelled"
  772.                    : "File Not Deleted",
  773.                  NULL);
  774.             break;
  775.         }
  776.         }
  777.  
  778.         if(status == TRUE){
  779.         if(unlink(child) < 0){
  780.             emlwrite("Delete Failed: %s", errstr(errno));
  781.         }
  782.         else{            /* fix up pointers and redraw */
  783.             tp = gmp->current;
  784.             if(tp->next){
  785.             gmp->current = tp->next;
  786.             if(tp->next->prev = tp->prev)
  787.               tp->prev->next = tp->next;
  788.             }
  789.             else if(tp->prev) {
  790.             gmp->current = tp->prev;
  791.             if(tp->prev->next = tp->next)
  792.               tp->next->prev = tp->prev;
  793.             }
  794.  
  795.             if(tp == gmp->head)
  796.               gmp->head = tp->next;
  797.  
  798.             tp->fname = NULL;
  799.             tp->next = tp->prev = NULL;
  800.             if(tp != gmp->current)
  801.               free((char *) tp);
  802.  
  803.             if((tp = FindCell(gmp, gmp->current->fname)) != NULL){
  804.             gmp->current = tp;
  805.             PlaceCell(gmp, gmp->current, &row, &col);
  806.             }
  807.                 
  808.             PaintBrowser(gmp, 1, &crow, &ccol);
  809.             mlerase();
  810.         }
  811.         }
  812.  
  813.         BrowserKeys();
  814.         break;
  815.  
  816.       case '?':                    /* HELP! */
  817.       case (CTRL|'G'):
  818.         if(term.t_mrow == 0){
  819.         if(km_popped == 0){
  820.             km_popped = 2;
  821.             break;
  822.         }
  823.         }
  824.  
  825.         if(Pmaster)
  826.           (*Pmaster->helper)(Pmaster->browse_help,
  827.                  "Help for Browsing", 1);
  828.         else if(gmode&MDBRONLY)
  829.           pico_help(sa_BrowseHelpText, "Browser Help", 1);
  830.         else
  831.           pico_help(BrowseHelpText, "Help for Browsing", 1);
  832.         /* fall thru to repaint everything */
  833.  
  834.       case (CTRL|'L'):
  835.         PaintBrowser(gmp, 0, &crow, &ccol);
  836.         break;
  837.  
  838.       case 'g':                             /* jump to a directory */
  839.       case 'G':
  840.  
  841.         if(!(gmode&MDGOTO))
  842.           goto Default;
  843.  
  844.         i = 0;
  845.         child[0] = '\0';
  846.  
  847.         while(!i){
  848.  
  849.         status = mlreply("Directory to go to: ", child, NLINE, QNORML,
  850.                  NULL);
  851.  
  852.         switch(status){
  853.           case HELPCH:
  854.             emlwrite("\007No help yet!", NULL);
  855.             /* remove break and sleep after help text is installed */
  856.             sleep(3);
  857.             break;
  858.           case (CTRL|'L'):
  859.             PaintBrowser(gmp, 0, &crow, &ccol);
  860.           break;
  861.           case ABORT:
  862.             emlwrite("Goto cancelled", NULL);
  863.             i++;
  864.             break;
  865.           case FALSE:
  866.           case TRUE:
  867.             i++;
  868.  
  869.             if(*child == '\0')
  870.               strcpy(child, gethomedir(NULL));
  871.  
  872.             if(!compresspath(gmp->dname, child, NLINE)){
  873.             emlwrite("Invalid Directory: %s", child);
  874.             break;
  875.             }
  876.  
  877.             if((gmode&MDSCUR) && homeless(child)){
  878.             emlwrite("Restricted mode browsing limited to home directory",NULL);
  879.             break;
  880.             }
  881.  
  882.             if((gmode&MDTREE) && !in_oper_tree(child)){
  883.             emlwrite("Attempt to Goto directory denied", NULL);
  884.             break;
  885.             }
  886.  
  887.             if(isdir(child, (long *) NULL)){
  888.             if((mp = getfcells(child)) == NULL){
  889.                 /* getfcells should explain what happened */
  890.                 i++;
  891.                 break;
  892.             }
  893.  
  894.             zotmaster(&gmp);
  895.             gmp = mp;
  896.             PaintBrowser(gmp, 0, &crow, &ccol);
  897.             }
  898.             else
  899.               emlwrite("\007Not a directory: \"%s\"", child);
  900.  
  901.             break;
  902.           default:
  903.             break;
  904.         }
  905.         }
  906.         BrowserKeys();
  907.         break;
  908.  
  909.       case 'c':                    /* copy */
  910.       case 'C':
  911.         if(gmp->current->mode == FIODIR){
  912.         emlwrite("\007Can't copy a directory", NULL);
  913.         break;
  914.         }
  915.  
  916.         if(gmode&MDSCUR){                /* not allowed! */
  917.         emlwrite("Copy not allowed in restricted mode",NULL);
  918.         break;
  919.         }
  920.  
  921.         i = 0;
  922.         child[0] = '\0';
  923.  
  924.         while(!i){
  925.  
  926.         switch(status=mlreply("Name of new copy: ", child, NLINE,
  927.                       QFFILE, NULL)){
  928.           case HELPCH:
  929.             emlwrite("\007No help yet!", NULL);
  930. /* remove break and sleep after help text is installed */
  931.             sleep(3);
  932.             break;
  933.           case (CTRL|'L'):
  934.             PaintBrowser(gmp, 0, &crow, &ccol);
  935.             break;
  936.           case ABORT:
  937.             emlwrite("Make Copy Cancelled", NULL);
  938.             i++;
  939.             break;
  940.           case FALSE:
  941.             i++;
  942.             mlerase();
  943.             break;
  944.           case TRUE:
  945.             i++;
  946.  
  947.             if(child[0] == '\0'){
  948.             emlwrite("No destination, file not copied", NULL);
  949.             break;
  950.             }
  951.  
  952.             if(!strcmp(gmp->current->fname, child)){
  953.             emlwrite("\007Can't copy file on to itself!", NULL);
  954.             break;
  955.             }
  956.  
  957.             strcpy(tmp, child);         /* add full path! */
  958.             sprintf(child, "%s%c%s", gmp->dname, C_FILESEP, tmp);
  959.  
  960.             if((status = fexist(child, "w", (long *)NULL)) == FIOSUC){
  961.             sprintf(tmp,"File \"%.*s\" exists! OVERWRITE",
  962.                 NLINE - 20, child);
  963.             if((status = mlyesno(tmp, 0)) != TRUE){
  964.                 emlwrite((status == ABORT)
  965.                       ? "Make copy cancelled" 
  966.                       : "File Not Renamed",
  967.                      NULL);
  968.                 break;
  969.             }
  970.             }
  971.             else if(status != FIOFNF){
  972.             fioperr(status, child);
  973.             break;
  974.             }
  975.  
  976.             sprintf(tmp, "%s%c%s", gmp->dname, C_FILESEP, 
  977.                 gmp->current->fname);
  978.  
  979.             if(copy(tmp, child) < 0){
  980.             /* copy()  will report any error messages */
  981.             break;
  982.             }
  983.             else{            /* highlight new file */
  984.             emlwrite("File copied to %s", child);
  985.  
  986.             if((p = strrchr(child, C_FILESEP)) == NULL){
  987.                 emlwrite("Problems refiguring browser", NULL);
  988.                 break;
  989.             }
  990.  
  991.             *p = '\0';
  992.             strcpy(tmp, (p == child) ? S_FILESEP: child);
  993.  
  994.             /*
  995.              * new file in same dir? if so, refigure files
  996.              * and redraw...
  997.              */
  998.             if(!strcmp(tmp, gmp->dname)){ 
  999.                 strcpy(child, gmp->current->fname);
  1000.                 if((mp = getfcells(gmp->dname)) == NULL)
  1001.                   /* getfcells should explain what happened */
  1002.                   break;
  1003.  
  1004.                 zotmaster(&gmp);
  1005.                 gmp = mp;
  1006.                 if((tp = FindCell(gmp, child)) != NULL){
  1007.                 gmp->current = tp;
  1008.                 PlaceCell(gmp, gmp->current, &row, &col);
  1009.                 }
  1010.  
  1011.                 PaintBrowser(gmp, 1, &crow, &ccol);
  1012.             }
  1013.             }
  1014.             break;
  1015.           default:
  1016.             break;
  1017.         }
  1018.         }
  1019.         BrowserKeys();
  1020.         break;
  1021.  
  1022.       case 'r':                    /* rename */
  1023.       case 'R':
  1024.         i = 0;
  1025.         child[0] = '\0';
  1026.  
  1027.         if(!strcmp(gmp->current->fname, "..")){
  1028.         emlwrite("\007Can't rename \"..\"", NULL);
  1029.         break;
  1030.         }
  1031.  
  1032.         if(gmode&MDSCUR){                /* not allowed! */
  1033.         emlwrite("Rename not allowed in restricted mode",NULL);
  1034.         break;
  1035.         }
  1036.  
  1037.         while(!i){
  1038.  
  1039.         switch(status=mlreply("Rename file to: ", child, NLINE, QFFILE,
  1040.                       NULL)){
  1041.           case HELPCH:
  1042.             emlwrite("\007No help yet!", NULL);
  1043. /* remove break and sleep after help text is installed */
  1044.             sleep(3);
  1045.             break;
  1046.           case (CTRL|'L'):
  1047.             PaintBrowser(gmp, 0, &crow, &ccol);
  1048.             break;
  1049.           case ABORT:
  1050.             emlwrite("Rename cancelled", NULL);
  1051.             i++;
  1052.             break;
  1053.           case FALSE:
  1054.           case TRUE:
  1055.             i++;
  1056.  
  1057.             if(child[0] == '\0' || status == FALSE){
  1058.             mlerase();
  1059.             break;
  1060.             }
  1061.  
  1062.             strcpy(tmp, child);
  1063.             sprintf(child, "%s%c%s", gmp->dname, C_FILESEP, tmp);
  1064.  
  1065.             status = fexist(child, "w", (long *)NULL);
  1066.             if(status == FIOSUC || status == FIOFNF){
  1067.             if(status == FIOSUC){
  1068.                 sprintf(tmp,"File \"%.*s\" exists! OVERWRITE",
  1069.                     NLINE - 20, child);
  1070.  
  1071.                 if((status = mlyesno(tmp, FALSE)) != TRUE){
  1072.                 emlwrite((status ==  ABORT)
  1073.                       ? "Rename cancelled"
  1074.                       : "Not Renamed",
  1075.                      NULL);
  1076.                 break;
  1077.                 }
  1078.             }
  1079.  
  1080.             sprintf(tmp, "%s%c%s", gmp->dname, C_FILESEP, 
  1081.                 gmp->current->fname);
  1082.  
  1083.             if(rename(tmp, child) < 0){
  1084.                 emlwrite("Rename Failed: %s", errstr(errno));
  1085.             }
  1086.             else{
  1087.                 if((p = strrchr(child, C_FILESEP)) == NULL){
  1088.                 emlwrite("Problems refiguring browser", NULL);
  1089.                 break;
  1090.                 }
  1091.                 
  1092.                 *p = '\0';
  1093.                 strcpy(tmp, (p == child) ? S_FILESEP: child);
  1094.  
  1095.                 if((mp = getfcells(tmp)) == NULL)
  1096.                   /* getfcells should explain what happened */
  1097.                   break;
  1098.  
  1099.                 zotmaster(&gmp);
  1100.                 gmp = mp;
  1101.  
  1102.                 if((tp = FindCell(gmp, ++p)) != NULL){
  1103.                 gmp->current = tp;
  1104.                 PlaceCell(gmp, gmp->current, &row, &col);
  1105.                 }
  1106.  
  1107.                 PaintBrowser(gmp, 1, &crow, &ccol);
  1108.                 mlerase();
  1109.             }
  1110.             }
  1111.             else{
  1112.             fioperr(status, child);
  1113.             }
  1114.             break;
  1115.           default:
  1116.             break;
  1117.         }
  1118.         }
  1119.         BrowserKeys();
  1120.         break;
  1121.  
  1122.       case 'v':                    /* stand-alone */
  1123.       case 'V':                    /* browser "view" */
  1124.       case 's':                    /* user "select" */
  1125.       case 'S':
  1126.       case (CTRL|'M'):
  1127.       Selected:
  1128.  
  1129.         if((toupper(new_c) == 'S' && (gmode&MDBRONLY))
  1130.            || (toupper(new_c) == 'V' && !(gmode&MDBRONLY)))
  1131.           goto Default;
  1132.  
  1133.         if(gmp->current->mode == FIODIR){
  1134.         *child = '\0';
  1135.         strcpy(tmp, gmp->dname);
  1136.         p = gmp->current->fname;
  1137.         if(p[0] == '.' && p[1] == '.' && p[2] == '\0'){
  1138.             if((p=strrchr(tmp, C_FILESEP)) != NULL){
  1139.             *p = '\0';
  1140.  
  1141.             if((gmode&MDTREE) && !in_oper_tree(tmp)){
  1142.                 emlwrite(
  1143.                    "\007Can't visit parent in restricted mode",
  1144.                    NULL);
  1145.                 break;
  1146.             }
  1147.  
  1148.             strcpy(child, &p[1]);
  1149.             if
  1150. #if defined(DOS) || defined(OS2)
  1151.               (p == tmp || (tmp[1] == ':' && tmp[2] == '\0'))
  1152. #else
  1153.               (p == tmp)
  1154. #endif
  1155.               {    /* is it root? */
  1156. #if defined(DOS) || defined(OS2)
  1157.                   if(*child)
  1158.                 strcat(tmp, S_FILESEP);
  1159. #else
  1160.                   if(*child)
  1161.                     strcpy(tmp, S_FILESEP);
  1162. #endif
  1163.                   else{
  1164.                   emlwrite("\007Can't move up a directory",
  1165.                        NULL);
  1166.                   break;
  1167.                   }
  1168.               }
  1169.             }
  1170.         }
  1171.         else{
  1172.             if(tmp[1] != '\0')        /* were in root? */
  1173.               strcat(tmp, S_FILESEP);
  1174.             strcat(tmp, gmp->current->fname);
  1175.         }
  1176.  
  1177.         if((mp = getfcells(tmp)) == NULL)
  1178.           /* getfcells should explain what happened */
  1179.           break;
  1180.  
  1181.         zotmaster(&gmp);
  1182.         gmp = mp;
  1183.         tp  = NULL;
  1184.  
  1185.         if(*child){
  1186.             if((tp = FindCell(gmp, child)) != NULL){
  1187.             gmp->current = tp;
  1188.             PlaceCell(gmp, gmp->current, &row, &col);
  1189.             }
  1190.             else
  1191.               emlwrite("\007Problem finding dir \"%s\"",child);
  1192.         }
  1193.  
  1194.         PaintBrowser(gmp, 0, &crow, &ccol);
  1195.         if(!*child)
  1196.           emlwrite("Select/View \".. parent dir\" to return to previous directory.",
  1197.                NULL);
  1198.  
  1199.         break;
  1200.         }
  1201.         else if(gmode&MDBRONLY){
  1202.         sprintf(child, "%s%c%s", gmp->dname, C_FILESEP, 
  1203.             gmp->current->fname);
  1204.  
  1205.         if(LikelyASCII(child)){
  1206.             char *pg = (char *) getenv("PAGER");
  1207.  
  1208.             sprintf(tmp, "%s %s", pg ? pg : BROWSER_PAGER, child);
  1209.             BrowserRunChild(tmp);
  1210.             PaintBrowser(gmp, 0, &crow, &ccol);
  1211.         }
  1212.  
  1213.         break;
  1214.         }
  1215.         else{                /* just return */
  1216.         strcpy(dir, gmp->dname);
  1217.         strcpy(fn, gmp->current->fname);
  1218.         if(sz != NULL)            /* size uninteresting */
  1219.           strcpy(sz, gmp->current->size);
  1220.  
  1221.         zotmaster (&gmp);
  1222.         return (1);
  1223.         }
  1224.         break;
  1225.  
  1226.       case 'w':                /* Where is */
  1227.       case 'W':
  1228.       case (CTRL|'W'):
  1229.         i = 0;
  1230.  
  1231.         while(!i){
  1232.  
  1233.         switch(readpattern("File name to find")){
  1234.           case HELPCH:
  1235.             emlwrite("\007No help yet!", NULL);
  1236. /* remove break and sleep after help text is installed */
  1237.             sleep(3);
  1238.             break;
  1239.           case (CTRL|'L'):
  1240.             PaintBrowser(gmp, 0, &crow, &ccol);
  1241.             break;
  1242.           case (CTRL|'Y'):        /* first first cell */
  1243.             for(tp = gmp->top; tp->prev; tp = tp->prev)
  1244.               ;
  1245.  
  1246.             i++;
  1247.             /* fall thru to repaint */
  1248.           case (CTRL|'V'):
  1249.             if(!i){
  1250.             do{
  1251.                 tp = gmp->top;
  1252.                 if((i = term.t_nrow - term.t_mrow - 2) <= 0)
  1253.                   break;
  1254.  
  1255.                 while(i-- && tp->next){
  1256.                 j = 0;
  1257.                 while(++j <= gmp->fpl  && tp->next)
  1258.                   tp = tp->next;
  1259.                 }
  1260.  
  1261.                 if(i < 0)
  1262.                   gmp->top = tp;
  1263.             }
  1264.             while(tp->next);
  1265.  
  1266.             emlwrite("Searched to end of directory", NULL);
  1267.             }
  1268.             else
  1269.               emlwrite("Searched to start of directory", NULL);
  1270.  
  1271.             if(tp){
  1272.             PlaceCell(gmp, gmp->current, &row, &col);
  1273.             PaintCell(row, col, gmp->cpf, gmp->current, 0);
  1274.             gmp->current = tp;
  1275.             if(PlaceCell(gmp, gmp->current, &row, &col)){
  1276.                 PaintBrowser(gmp, 1, &crow, &ccol);
  1277.             }
  1278.             else{
  1279.                 PaintCell(row, col, gmp->cpf, gmp->current, 1);
  1280.                 crow = row;
  1281.                 ccol = col;
  1282.             }
  1283.             }
  1284.  
  1285.             i++;            /* make sure we jump out */
  1286.             break;
  1287.           case ABORT:
  1288.             emlwrite("Whereis cancelled", NULL);
  1289.             i++;
  1290.             break;
  1291.           case FALSE:
  1292.             mlerase();
  1293.             i++;
  1294.             break;
  1295.           case TRUE:
  1296.             if((tp = FindCell(gmp, pat)) != NULL){
  1297.             PlaceCell(gmp, gmp->current, &row, &col);
  1298.             PaintCell(row, col, gmp->cpf, gmp->current, 0);
  1299.             gmp->current = tp;
  1300.  
  1301.             if(PlaceCell(gmp, tp, &row, &col)){ /* top changed */
  1302.                 PaintBrowser(gmp, 1, &crow, &ccol);
  1303.             }
  1304.             else{
  1305.                 PaintCell(row, col, gmp->cpf, gmp->current, 1);
  1306.                 crow = row;
  1307.                 ccol = col;
  1308.             }
  1309.             mlerase();
  1310.             }
  1311.             else
  1312.               emlwrite("\"%s\" not found", pat);
  1313.  
  1314.             i++;
  1315.             break;
  1316.           default:
  1317.             break;
  1318.         }
  1319.         }
  1320.  
  1321.         BrowserKeys();
  1322.         break;
  1323.  
  1324.       case (CTRL|'Z'):
  1325.         if(gmode&MDSSPD){
  1326.         bktoshell();
  1327.         PaintBrowser(gmp, 0, &crow, &ccol);
  1328.         break;
  1329.         }                    /* fall thru with error! */
  1330.  
  1331.       default:                /* what? */
  1332.       Default:
  1333.         if(c < 0xff)
  1334.           emlwrite("\007Unknown command: '%c'", (void *) c);
  1335.         else if(c & CTRL)
  1336.           emlwrite("\007Unknown command: ^%c", (void *)(c&0xff));
  1337.         else
  1338.           emlwrite("\007Unknown command", NULL);
  1339.       case NODATA:                /* no op */
  1340.         break;
  1341.     }
  1342.     }
  1343. }
  1344.  
  1345.  
  1346.  
  1347. /*
  1348.  * getfcells - make a master browser struct and fill it in
  1349.  *             return NULL if there's a problem.
  1350.  */
  1351. struct bmaster *
  1352. getfcells(dname)
  1353.     char *dname;
  1354. {
  1355.     int  i,                     /* various return codes */
  1356.          nentries = 0;                /* number of dir ents */
  1357.     long l;
  1358.     char *np,                    /* names of files in dir */
  1359.          *dcp,                    /* to add file to path */
  1360.          **filtnames,                /* array filtered names */
  1361.      errbuf[NLINE];
  1362.     struct fcell *ncp,                /* new cell pointer */
  1363.                  *tcp;                /* trailing cell ptr */
  1364.     struct bmaster *mp;
  1365.  
  1366.     errbuf[0] = '\0';
  1367.     if((mp=(struct bmaster *)malloc(sizeof(struct bmaster))) == NULL){
  1368.     emlwrite("\007Can't malloc space for master filename cell", NULL);
  1369.     return(NULL);
  1370.     }
  1371.  
  1372.     if(dname[0] == '.' && dname[1] == '\0'){        /* remember this dir */
  1373.     if(!getcwd(mp->dname, 256))
  1374.       mp->dname[0] = '\0';
  1375.     }
  1376.     else if(dname[0] == '.' && dname[1] == '.' && dname[2] == '\0'){
  1377.     if(!getcwd(mp->dname, 256))
  1378.       mp->dname[0] = '\0';
  1379.     else{
  1380.         if((np = (char *)strrchr(mp->dname, C_FILESEP)) != NULL)
  1381.           if(np != mp->dname)
  1382.         *np = '\0';
  1383.     }
  1384.     }
  1385.     else
  1386.       strcpy(mp->dname, dname);
  1387.  
  1388.     mp->head = mp->top = NULL;
  1389.     mp->cpf = mp->fpl = 0;
  1390.     mp->longest = 5;                /* .. must be labeled! */
  1391.  
  1392.     emlwrite("Building file list of %s...", mp->dname);
  1393.  
  1394.     if((mp->names = getfnames(mp->dname, NULL, &nentries, errbuf)) == NULL){
  1395.     free((char *) mp);
  1396.     if(*errbuf)
  1397.       emlwrite(errbuf, NULL);
  1398.  
  1399.     return(NULL);
  1400.     }
  1401.  
  1402.     /*
  1403.      * this is the fun part.  build an array of pointers to the fnames we're
  1404.      * interested in (i.e., do any filtering), then pass that off to be
  1405.      * sorted before building list of cells...
  1406.      *
  1407.      * right now default filtering on ".*" except "..", but this could
  1408.      * easily be made a user option later on...
  1409.      */
  1410.     if((filtnames=(char **)malloc((nentries+1) * sizeof(char *))) == NULL){
  1411.     emlwrite("\007Can't malloc space for name array", NULL);
  1412.     zotmaster(&mp);
  1413.     return(NULL);
  1414.     }
  1415.  
  1416.     i = 0;                    /* index of filt'd array */
  1417.     np = mp->names;
  1418.     while(nentries--){
  1419.     /*
  1420.      * Filter dot files?  Always filter ".", never filter "..",
  1421.      * and sometimes fitler ".*"...
  1422.      */
  1423.     if(*np == '.' && !(*(np+1) == '.' && *(np+2) == '\0')
  1424.        && (*(np+1) == '\0' || !(gmode & MDDOTSOK))){
  1425.         np += strlen(np) + 1;
  1426.         continue;
  1427.     }
  1428.  
  1429.     filtnames[i++] = np;
  1430.  
  1431.     if((l = strlen(np)) > mp->longest)    /* cast l ? */
  1432.       mp->longest = l;            /* remember longest */
  1433.     np += l + 1;                /* advance name pointer */
  1434.  
  1435.     }
  1436.     nentries = i;                /* new # of entries */
  1437.  
  1438.     /* 
  1439.      * sort files case independently
  1440.      */
  1441.     qsort((QSType *)filtnames, (size_t)nentries, sizeof(char *), sstrcasecmp);
  1442.  
  1443.     /* 
  1444.      * this is so we use absolute path names for stats.
  1445.      * remember: be careful using dname as directory name, and fix mp->dname
  1446.      * when we're done
  1447.      */
  1448.     dcp = (char *)strchr(mp->dname, '\0');
  1449.     if(dcp == mp->dname || dcp[-1] != C_FILESEP){
  1450.     dcp[0] = C_FILESEP;
  1451.     dcp[1] = '\0';
  1452.     }
  1453.     else
  1454.       dcp--;
  1455.  
  1456.     i = 0;
  1457.     while(nentries--){                /* stat filtered files */
  1458.     /* get a new cell */
  1459.     if((ncp=(struct fcell *)malloc(sizeof(struct fcell))) == NULL){
  1460.         emlwrite("\007Can't malloc cells for browser!", NULL);
  1461.         zotfcells(mp->head);        /* clean up cells */
  1462.         free((char *) filtnames);
  1463.         free((char *) mp);
  1464.         return(NULL);            /* bummer. */
  1465.     }
  1466.     ncp->next = ncp->prev = NULL;
  1467.  
  1468.     if(mp->head == NULL){            /* tie it onto the list */
  1469.         mp->head = mp->top = mp->current = ncp;
  1470.     }
  1471.     else{
  1472.         tcp->next = ncp;
  1473.         ncp->prev = tcp;
  1474.     }
  1475.     tcp = ncp;
  1476.  
  1477.     /* fill in the new cell */
  1478.     ncp->fname = filtnames[i++];
  1479.  
  1480.     strcpy(&dcp[1], ncp->fname);        /* use abolute path! */
  1481.  
  1482.     /* fill in file's mode */
  1483.     switch(fexist(mp->dname, "t", &l)){
  1484.       case FIODIR :
  1485.         ncp->mode = FIODIR;
  1486.         sprintf(ncp->size, "(%sdir)",
  1487.             (ncp->fname[0] == '.' && ncp->fname[1] == '.'
  1488.              && ncp->fname[2] == '\0') ? "parent " : "");
  1489.         break;
  1490.       case FIOSYM :
  1491.         ncp->mode = FIOSYM;
  1492.         strcpy(ncp->size, "--");
  1493.         break;
  1494.       default :
  1495.         ncp->mode = FIOSUC;            /* regular file */
  1496.         strcpy(ncp->size,  prettysz(l));
  1497.         break;
  1498.     }
  1499.     }
  1500.  
  1501.     dcp[(dcp == mp->dname) ? 1 : 0] = '\0';    /* remember to cap dname */
  1502.     free((char *) filtnames);            /* 'n blast filt'd array*/
  1503.  
  1504.     percdircells(mp);
  1505.     layoutcells(mp);
  1506.     return(mp);
  1507. }
  1508.  
  1509.  
  1510.  
  1511. /*
  1512.  * PaintCell - print the given cell at the given location on the display
  1513.  *             the format of a printed cell is:
  1514.  *
  1515.  *                       "<fname>       <size>  "
  1516.  */
  1517. PaintCell(x, y, l, cell, inverted)
  1518. struct fcell *cell;
  1519. int    x, y, l, inverted;
  1520. {
  1521.     char *p;                    /* temp str pointer */
  1522.     int   i = 0,                /* current display count */
  1523.           j, sl, fl;                /* lengths */
  1524.  
  1525.     if(cell == NULL)
  1526.     return(-1);
  1527.  
  1528.     fl = strlen(cell->fname);
  1529.     sl = strlen(cell->size);
  1530.  
  1531.     movecursor(x, y);
  1532.     if(inverted)
  1533.       (*term.t_rev)(1);
  1534.     
  1535.     /* room for fname? */
  1536.     p = (fl+2 > l) ? &cell->fname[fl-(l-2)] : cell->fname;
  1537.     while(*p != '\0'){                /* write file name */
  1538.     pputc(*p++, 0);
  1539.     i++;
  1540.     }
  1541.  
  1542.     if(sl+3 <= l-i){                /* room for size? */
  1543.     j = (l-i)-(sl+2);            /* put space between */
  1544.     i += j;
  1545.     while(j--)                /* file name and size */
  1546.       pputc(' ', 0);
  1547.  
  1548.     p = cell->size;
  1549.     while(*p != '\0'){            /* write file size */
  1550.         pputc(*p++, 0);
  1551.         i++;
  1552.     }
  1553.     }
  1554.  
  1555.     if(inverted)
  1556.       (*term.t_rev)(0);
  1557.  
  1558.     while(i++ < l)                /* pad ending */
  1559.       pputc(' ', 0);
  1560.  
  1561.     return(1);
  1562. }
  1563.  
  1564.  
  1565.  
  1566. /*
  1567.  * PaintBrowse - with the current data, display the browser.  if level == 0 
  1568.  *               paint the whole thing, if level == 1 just paint the cells
  1569.  *               themselves
  1570.  */
  1571. PaintBrowser(mp, level, row, col)
  1572. struct bmaster *mp;
  1573. int level;
  1574. int *row, *col;
  1575. {
  1576.     int i, cl;
  1577.     struct fcell *tp;
  1578.  
  1579.     if(!level){
  1580.     ClearBrowserScreen();
  1581.     BrowserAnchor(mp->dname);
  1582.     }
  1583.  
  1584.     i = 0;
  1585.     tp = mp->top;
  1586.     cl = COMPOSER_TOP_LINE;            /* current display line */
  1587.     while(tp){
  1588.  
  1589.     PaintCell(cl, mp->cpf * i, mp->cpf, tp, tp == mp->current);
  1590.  
  1591.     if(tp == mp->current){
  1592.         if(row)
  1593.           *row = cl;
  1594.  
  1595.         if(col)
  1596.           *col = mp->cpf * i;
  1597.     }
  1598.  
  1599.     if(++i >= mp->fpl){
  1600.         i = 0;
  1601.         if(++cl > term.t_nrow-(term.t_mrow+1))
  1602.           break;
  1603.     }
  1604.  
  1605.     tp = tp->next;
  1606.     }
  1607.  
  1608.     if(level){
  1609.     while(cl <= term.t_nrow - (term.t_mrow+1)){
  1610.         if(!i)
  1611.           movecursor(cl, 0);
  1612.         peeol();
  1613.         movecursor(++cl, 0);
  1614.     }
  1615.     }
  1616.     else{
  1617.     BrowserKeys();
  1618.     }
  1619.  
  1620.     return(1);
  1621. }
  1622.  
  1623.  
  1624. /*
  1625.  * BrowserKeys - just paints the keyhelp at the bottom of the display
  1626.  */
  1627. BrowserKeys()
  1628. {
  1629.     menu_browse[QUIT_KEY].name  = (gmode&MDBRONLY) ? "Q" : "E";
  1630.     menu_browse[QUIT_KEY].label = (gmode&MDBRONLY) ? "Quit" : "Exit Brwsr";
  1631.     menu_browse[GOTO_KEY].name  = (gmode&MDGOTO) ? "G" : NULL;
  1632.     menu_browse[GOTO_KEY].label = (gmode&MDGOTO) ? "Goto" : NULL;
  1633.     if(gmode & MDBRONLY){
  1634.     menu_browse[EXEC_KEY].name  = "L";
  1635.     menu_browse[EXEC_KEY].label = "Launch";
  1636.     menu_browse[SELECT_KEY].name  = "V";
  1637.     menu_browse[SELECT_KEY].label = "[View]";
  1638.     menu_browse[PICO_KEY].name  = "E";
  1639.     menu_browse[PICO_KEY].label = "Edit";
  1640.     }
  1641.     else{
  1642.     menu_browse[SELECT_KEY].name  = "S";
  1643.     menu_browse[SELECT_KEY].label = "[Select]";
  1644.     }
  1645.  
  1646.     wkeyhelp(menu_browse);
  1647. }
  1648.  
  1649.  
  1650. /*
  1651.  * layoutcells - figure out max length of cell and how many cells can 
  1652.  *               go on a line of the display
  1653.  */
  1654. layoutcells(mp)
  1655. struct bmaster *mp;
  1656. {
  1657.     mp->cpf = mp->longest + 12;            /* max chars / file */
  1658.     if(gmode & MDONECOL){
  1659.     mp->fpl = 1;
  1660.     }
  1661.     else{
  1662.     int i = 1;
  1663.  
  1664.     while(i*mp->cpf < term.t_ncol)        /* no force... */
  1665.       i++;                    /* like brute force! */
  1666.  
  1667.     mp->fpl = i - 1;            /* files per line */
  1668.     }
  1669. }
  1670.  
  1671.  
  1672. /*
  1673.  * percdircells - bubble all the directory cells to the top of the
  1674.  *                list.
  1675.  */
  1676. percdircells(mp)
  1677. struct bmaster *mp;
  1678. {
  1679.     struct fcell *dirlp,            /* dir cell list pointer */
  1680.                  *lp, *nlp;            /* cell list ptr and next */
  1681.  
  1682.     dirlp = NULL;
  1683.     for(lp = mp->head; lp; lp = nlp){
  1684.     nlp = lp->next;
  1685.     if(lp->mode == FIODIR){
  1686.         if(lp->prev)            /* clip from list */
  1687.           lp->prev->next = lp->next;
  1688.  
  1689.         if(lp->next)
  1690.           lp->next->prev = lp->prev;
  1691.  
  1692.         if(lp->prev = dirlp){        /* tie it into dir portion */
  1693.         if(lp->next = dirlp->next)
  1694.           lp->next->prev = lp;
  1695.  
  1696.         dirlp->next = lp;
  1697.         dirlp = lp;
  1698.         }
  1699.         else{
  1700.         if((dirlp = lp) != mp->head)
  1701.           dirlp->next = mp->head;
  1702.  
  1703.         if(dirlp->next)
  1704.           dirlp->next->prev = dirlp;
  1705.  
  1706.         mp->head = mp->top = mp->current = dirlp;
  1707.         }
  1708.     }
  1709.     }
  1710. }
  1711.  
  1712.  
  1713. /*
  1714.  * PlaceCell - given a browser master, return row and col of the display that
  1715.  *             it should go.  
  1716.  *
  1717.  *             return 1 if mp->top has changed, x,y relative to new page
  1718.  *             return 0 if otherwise (same page)
  1719.  *             return -1 on error
  1720.  */
  1721. PlaceCell(mp, cp, x, y)
  1722. struct bmaster *mp;
  1723. struct fcell *cp;
  1724. int    *x, *y;
  1725. {
  1726.     int cl = COMPOSER_TOP_LINE;            /* current line */
  1727.     int ci = 0;                    /* current index on line */
  1728.     int rv = 0;
  1729.     int secondtry = 0;
  1730.     struct fcell *tp;
  1731.  
  1732.     /* will cp fit on screen? */
  1733.     tp = mp->top;
  1734.     while(1){
  1735.     if(tp == cp){                /* bingo! */
  1736.         *x = cl;
  1737.         *y = ci * mp->cpf;
  1738.         break;
  1739.     }
  1740.  
  1741.     if((tp = tp->next) == NULL){        /* above top? */
  1742.         if(secondtry++){
  1743.         emlwrite("\007Internal error: can't find fname cell", NULL);
  1744.         return(-1);
  1745.         }
  1746.         else{
  1747.         tp = mp->top = mp->head;    /* try from the top! */
  1748.         cl = COMPOSER_TOP_LINE;
  1749.         ci = 0;
  1750.         rv = 1;
  1751.         continue;            /* start over! */
  1752.         }
  1753.     }
  1754.  
  1755.     if(++ci >= mp->fpl){            /* next line? */
  1756.         ci = 0;
  1757.         if(++cl > term.t_nrow-(term.t_mrow+1)){ /* next page? */
  1758.         ci = mp->fpl;            /* tp is at bottom right */
  1759.         while(ci--)            /* find new top */
  1760.           tp = tp->prev;
  1761.         mp->top = tp;
  1762.         ci = 0;
  1763.         cl = COMPOSER_TOP_LINE;        /* keep checking */
  1764.         rv = 1;
  1765.         }
  1766.     }
  1767.  
  1768.     }
  1769.  
  1770.     /* not on display! */
  1771.     return(rv);
  1772.     
  1773. }
  1774.  
  1775.  
  1776. /*
  1777.  * zotfcells - clean up malloc'd cells of file names
  1778.  */
  1779. zotfcells(hp)
  1780. struct fcell *hp;
  1781. {
  1782.     struct fcell *tp;
  1783.  
  1784.     while(hp){
  1785.     tp = hp;
  1786.     hp = hp->next;
  1787.     tp->next = NULL;
  1788.     free((char *) tp);
  1789.     }
  1790. }
  1791.  
  1792.  
  1793. /*
  1794.  * zotmaster - blast the browser master struct
  1795.  */
  1796. zotmaster(mp)
  1797. struct bmaster **mp;
  1798. {
  1799.     zotfcells((*mp)->head);            /* free cells       */
  1800.     free((char *)(*mp)->names);            /* free file names  */
  1801.     free((char *)*mp);                /* free master      */
  1802.     *mp = NULL;                    /* make double sure */
  1803. }
  1804.  
  1805.  
  1806. /*
  1807.  * FindCell - starting from the current cell find the first occurance of 
  1808.  *            the given string wrapping around if necessary
  1809.  */
  1810. struct fcell *FindCell(mp, string)
  1811. struct bmaster *mp;
  1812. char   *string;
  1813. {
  1814.     struct fcell *tp, *fp;
  1815.  
  1816.     if(*string == '\0')
  1817.       return(NULL);
  1818.  
  1819.     fp = NULL;
  1820.     tp = mp->current->next;
  1821.     
  1822.     while(tp && !fp){
  1823.     if(sisin(tp->fname, string))
  1824.       fp = tp;
  1825.     else
  1826.       tp = tp->next;
  1827.     }
  1828.  
  1829.     tp = mp->head;
  1830.     while(tp != mp->current && !fp){
  1831.     if(sisin(tp->fname, string))
  1832.       fp = tp;
  1833.     else
  1834.       tp = tp->next;
  1835.     }
  1836.  
  1837.     return(fp);
  1838. }
  1839.  
  1840.  
  1841. /*
  1842.  * sisin - case insensitive substring matching function
  1843.  */
  1844. sisin(s1, s2)
  1845. char *s1, *s2;
  1846. {
  1847.     register int j;
  1848.  
  1849.     while(*s1){
  1850.     j = 0;
  1851.     while(toupper((unsigned char)s1[j]) == toupper((unsigned char)s2[j]))
  1852.       if(s2[++j] == '\0')            /* bingo! */
  1853.         return(1);
  1854.  
  1855.     s1++;
  1856.     }
  1857.     return(0);
  1858. }
  1859.  
  1860.  
  1861. /*
  1862.  * set_browser_title - 
  1863.  */
  1864. set_browser_title(s)
  1865. char *s;
  1866. {
  1867.     browser_title = s;
  1868. }
  1869.  
  1870.  
  1871. /*
  1872.  * BrowserAnchor - draw the browser's anchor line.
  1873.  */
  1874. BrowserAnchor(dir)
  1875. char *dir;
  1876. {
  1877.     register char *p;
  1878.     register int  i, j, l;
  1879.     char          buf[NLINE];
  1880.  
  1881.     movecursor(0, 0);
  1882.     (*term.t_rev)(1);
  1883.  
  1884.     i = 0;
  1885.     l = strlen(dir);
  1886.     j = (term.t_ncol-(l+16))/2;
  1887.  
  1888.     if(browser_title)
  1889.       sprintf(buf, "   %s", browser_title);
  1890.     else if(Pmaster)
  1891.       sprintf(buf, "   PINE %s", Pmaster->pine_version);
  1892.     else
  1893.       sprintf(buf,"   UW PICO(tm) %s", (gmode&MDBRONLY) ? "BROWSER" : version);
  1894.  
  1895.     p = buf;
  1896.     while(*p){
  1897.     pputc(*p++, 0);
  1898.     i++;
  1899.     }
  1900.  
  1901.     if(l > term.t_ncol - i - 21){        /* fit dir name on line */
  1902.     p = dir;
  1903.     while((p = strchr(p, C_FILESEP)) && (l-(p-dir)) > term.t_ncol-i-21)
  1904.       p++;
  1905.  
  1906.     if(!*p)                    /* no suitable length! */
  1907.       p = &dir[l-(term.t_ncol-i-19)];
  1908.  
  1909.     sprintf(buf, "%s Dir ...%s", (gmode&MDBRONLY) ? "" : " BROWSER  ", p);
  1910.     }
  1911.     else 
  1912.       sprintf(buf,"%s  Dir: %s", (gmode&MDBRONLY) ? "" : " BROWSER  ", dir);
  1913.  
  1914.     if(i < j)                    /* keep it centered */
  1915.       j = j - i;                /* as long as we can */
  1916.     else
  1917.       j = ((term.t_ncol-i)-((int)strlen(p)+15))/2;
  1918.  
  1919.     while(j-- && i++)
  1920.       pputc(' ', 0);
  1921.  
  1922.     p = buf;
  1923.     while(i++ < term.t_ncol && *p)        /* show directory */
  1924.       pputc(*p++, 0);
  1925.  
  1926.     while(i++ < term.t_ncol)
  1927.       pputc(' ', 0);
  1928.  
  1929.     (*term.t_rev)(0);
  1930. }
  1931.  
  1932.  
  1933. /*
  1934.  * ResizeBrowser - handle a resize event
  1935.  */
  1936. ResizeBrowser()
  1937. {
  1938.     if(gmp){
  1939.     layoutcells(gmp);
  1940.     PaintBrowser(gmp, 0, NULL, NULL);
  1941.     return(1);
  1942.     }
  1943.     else
  1944.       return(0);
  1945. }
  1946.  
  1947.  
  1948. void
  1949. ClearBrowserScreen()
  1950. {
  1951.     int i;
  1952.  
  1953.     for(i = 0; i <= term.t_nrow; i++){        /* clear screen */
  1954.     movecursor(i, 0);
  1955.     peeol();
  1956.     }
  1957. }
  1958.  
  1959.  
  1960. void
  1961. BrowserRunChild(child)
  1962.     char *child;
  1963. {
  1964.     int  status;
  1965.     char tmp[NLINE];
  1966.  
  1967.     ClearBrowserScreen();
  1968.     movecursor(0, 0);
  1969.     (*term.t_close)();
  1970.     fflush(stdout);
  1971.     status = system(child);
  1972.     (*term.t_open)();
  1973.     /* complain about non-zero exit status */
  1974.     if((status >> 8) & 0xff){
  1975.  
  1976.     movecursor(term.t_nrow - 1, 0);
  1977.     sprintf(tmp, "[ \"%.30s\" exit with error value: %d ]",
  1978.         child, (status >> 8) & 0xff);
  1979.     pputs(tmp, 1);
  1980.     movecursor(term.t_nrow, 0);
  1981.     pputs("[ Hit RETURN to continue ]", 1);
  1982.     fflush(stdout);
  1983.  
  1984.     while(GetKey() != (CTRL|'M')){
  1985.         (*term.t_beep)();
  1986.         fflush(stdout);
  1987.     }
  1988.     }
  1989. }
  1990. #endif    /* _WINDOWS */
  1991.  
  1992.  
  1993. /*
  1994.  * LikelyASCII - make a rough guess as to the displayability of the
  1995.  *         given file.
  1996.  */
  1997. int
  1998. LikelyASCII(file)
  1999.     char *file;
  2000. {
  2001. #define    LA_TEST_BUF    1024
  2002. #define    LA_LINE_LIMIT    300
  2003. #if defined(DOS) || defined(OS2)
  2004. #define    MODE    "rb"
  2005. #else
  2006. #define    MODE    "r"
  2007. #endif
  2008.     int           n, i, line, rv = FALSE;
  2009.     unsigned char  buf[LA_TEST_BUF];
  2010.     FILE      *fp;
  2011.  
  2012.     if(fp = fopen(file, "rb")){
  2013.     clearerr(fp);
  2014.     if((n = fread(buf, sizeof(char), LA_TEST_BUF * sizeof(char), fp)) > 0
  2015.        || !ferror(fp)){
  2016.         /*
  2017.          * If we don't hit any newlines in a reasonable number,
  2018.          * LA_LINE_LIMIT, of characters or the file contains NULLs,
  2019.          * bag out...
  2020.          */
  2021.         rv = TRUE;
  2022.         for(i = line = 0; i < n; i++)
  2023.           if((line = (buf[i] == '\n') ? 0 : line + 1) >= LA_LINE_LIMIT
  2024.          || !buf[i]){
  2025.           rv = FALSE;
  2026.           emlwrite("Can't display non-text file.  Try \"Launch\".",
  2027.                NULL);
  2028.           break;
  2029.           }
  2030.     }
  2031.     else
  2032.       emlwrite("Can't read file: %s", file);
  2033.  
  2034.     fclose(fp);
  2035.     }
  2036.     else
  2037.       emlwrite("Can't open file: %s", file);
  2038.  
  2039.     return(rv);
  2040. }
  2041.